﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Stauffware.Common.Core
{
	//#################################################################################
	/// <summary>
	/// Abstract base class for parsing non-reference numeric types.
	/// </summary>
	/// <typeparam name="Tvalue">the type of the parsed result value</typeparam>
	/// <typeparam name="Tparse">the type of the Parse result</typeparam>

	public abstract class NumberParser<Tvalue, Tparse> where Tvalue : struct where Tparse : NumberParse<Tvalue>, new()
	{
		/// <summary>
		/// Enum bits reserved for NumberParseOptions.  
		/// "Derived" parse option enums must not redefine these.
		/// </summary>
		internal const int Reserved_ParseOptions_Bits = 0xFFFF00;

		/// <summary>
		/// What to use when there isn't a NumberParseOptions parameter.
		/// </summary>
		public const NumberParseOptions DefaultParseOptions = 
			NumberParseOptions.AllowWhiteSpace | NumberParseOptions.ParseEntireInput | 
			NumberParseOptions.AllowExponent | NumberParseOptions.AllowDecimalPoint | 
			NumberParseOptions.AllowLeadingSign | NumberParseOptions.AllowTrailingSign | 
			NumberParseOptions.AllowGrouping;

		/// <summary>
		/// The parse options used by this parser.
		/// </summary>
		protected readonly NumberParseOptions ParseOptions;

		/// <summary>
		/// The numeric formatting info used by this parser.
		/// </summary>
		protected readonly System.Globalization.NumberFormatInfo FormatInfo;

		/// <summary>
		/// The lexer for this parser.
		/// </summary>
		protected readonly NumberLexer Lexer;

		//-----------------------------------------------------------------------------
		/// <summary>
		/// Constructs a parser.
		/// </summary>
		/// <param name="options">parsing options</param>
		/// <param name="ifp">formatting info, or null</param>

		protected NumberParser (NumberParseOptions options, IFormatProvider ifp)
		{
			if (!this.Signed)
				options &= ~NumberParseOptions.AllowAnySign;

			this.ParseOptions = options;
			this.FormatInfo = System.Globalization.NumberFormatInfo.GetInstance(ifp);
			this.Lexer = new NumberLexer(this.ParseOptions, this.FormatInfo, 
										 this.MaxDigits, this.MaxExponentDigits);
		}

		//-----------------------------------------------------------------------------
		/// <summary>
		/// Parses a number.
		/// </summary>
		/// <param name="input">the string to be parsed</param>

		public Tparse Parse (string input)
		{
			Require.NotNull(input);

			bool lexedAnumber = this.Lexer.LexNumber(input);

		//	NumberParse<Tvalue> parse = this.CreateParseObject();
			Tparse parse = new Tparse();
			parse._parsedText = input.Substring(0, this.Lexer.CharsLexed);
			parse._errorMessage = this.Lexer.ErrorMessage;

			if (!lexedAnumber)
				return parse;

			string rawNumber;
			if (!this.AssembleNormalizedNumber(out rawNumber, out parse._errorMessage))
				return parse;

			Tvalue tempValue;
			if (this.TryParse(rawNumber, out tempValue))
				parse._value = tempValue;
			else
			{
				// NumberLexer has already checked for all possible syntax errors,
				// so the only thing left that could go wrong is a numeric overflow.
				parse._errorMessage = "magnitude of number is too large";
			}

			return parse;
		}

		//-----------------------------------------------------------------------------

		#region abstract properties & methods

		/// <summary>
		/// Gets the maximum number of digits (base 10) that a number can have
		/// (not including the exponent).
		/// </summary>
		protected abstract int MaxDigits { get; }

		/// <summary>
		/// Gets the maximum number of digits (base 10) that a number's exponent can have.
		/// </summary>
		protected abstract int MaxExponentDigits { get; }

		/// <summary>
		/// Gets whether this parser's target type is signed.
		/// </summary>
		protected virtual bool Signed { get { return true; } }

		///// <summary>
		///// Creates a Parse object of the proper class for this Parser.
		///// </summary>
		///// <returns></returns>
	//	protected abstract NumberParse<Tvalue> CreateParseObject ( );

		/// <summary>
		/// Assembles selected tokens from this.Lexer into a normalized number 
		/// string (no digit separators or whitespace).
		/// </summary>
		/// <param name="number">receives the normalized number</param>
		/// <param name="error">receives an error string (if false is returned)</param>
		/// <returns>true on success, false on error</returns>
		protected abstract bool AssembleNormalizedNumber (out string number, out string error);

		/// <summary>
		/// Attempts a parse of a normalized ("raw") number string.
		/// </summary>
		/// <param name="str">the string to parse</param>
		/// <param name="value">receives the parsed value</param>
		/// <returns>true on success, false on failure</returns>
		protected abstract bool TryParse (string str, out Tvalue value);

		#endregion
	}

	//#################################################################################
	/// <summary>
	/// Encapsulates the result of trying to parse a string into a non-reference numeric type.
	/// </summary>

	public abstract class NumberParse<T> : ValueParse<T> where T : struct
	{


	}

	//#################################################################################
}
